package com.vteba.utils.web.http;

import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.vteba.utils.charstr.ByteUtils;
import com.vteba.utils.charstr.Char;
import com.vteba.utils.common.PropUtils;
import com.vteba.utils.io.IOUtils;
import com.vteba.utils.json.FastJsonUtils;

/**
 * Java原生的Http请求的工具方法，对HttpsUrlConnection的封装。
 * 
 * @author yinlei
 * @see
 * @since 2015年4月21日 下午7:04:19
 */
public class HttpsTools {
	private static final Logger LOGGER = LoggerFactory.getLogger(HttpsTools.class);
	private static final String HTTPS_PREFIX;
	private static final boolean BRIEF;
	static {
		initSSL();
		HTTPS_PREFIX = PropUtils.get(Consts.HTTPS_PREFIX, "https://localhost:8443/");
		BRIEF = PropUtils.getBoolean(Consts.HTTPS_BRIEF, true);
	}
	
	/**
	 * 初始化https的设置，ssl链接设置。
	 */
	private static void initSSL() {
        TrustManager[] trustManagers = new TrustManager[1];
        trustManagers[0] = new SimpleX509TrustManager();
		try {
            SSLContext sslContext = SSLContext.getInstance("SSL");
			sslContext.init(null, trustManagers, null);
			HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
			
			HostnameVerifier hostnameVerifier = new SimpleHostnameVerifier();
			HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
		} catch (Exception e) {
			LOGGER.error("init ssl for https error, msg=[{}].", e.getMessage());
		}
	}
	
	/**
	 * 创建https链接
	 * @param url url
	 * @param method 请求方法
	 * @return https 链接
	 */
	private static HttpsURLConnection getHttpsConnection(URL url, HttpMethod method) {
		HttpsURLConnection connection = null;
		try {		
			connection = (HttpsURLConnection) url.openConnection();
			connection.setDoInput(true);
			connection.setDoOutput(true);
			connection.setConnectTimeout(4 * 1000);
			connection.setReadTimeout(5 * 1000);
			connection.setRequestMethod(method.name());
		} catch (Exception e) {
			LOGGER.error("build https url connection error, msg=[{}].", e.getMessage());
		}
		return connection;
	}
	
	/**
	 * 创建https链接，用于上传文件
	 * @param url url
	 * @return https 链接
	 */
	private static HttpsURLConnection getHttpsConnection(URL url) {
		HttpsURLConnection connection = getHttpsConnection(url, HttpMethod.POST);
		connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + Consts.BOUNDARY);
		return connection;
	}
	
	/**
	 * 执行get请求
	 * @param url 请求的url，不含前缀
	 * @param params 参数
	 * @param resultClass 结果泛型类
	 * @return 调用结果
	 */
	public static <T> T getBean(String url, Map<String, String> params, Class<T> resultClass) {
		byte[] result = get(url, params);
		if (result == null) {
			return null;
		}
		T t = FastJsonUtils.fromJson(result, resultClass);
		return t;
	}
	
	/**
	 * 执行get请求
	 * @param url 请求的url，不含前缀
	 * @param params 参数
	 * @param resultClass 结果泛型类
	 * @return 调用结果
	 */
	public static <T> List<T> getList(String url, Map<String, String> params, Class<T> resultClass) {
		byte[] result = get(url, params);
		if (result == null) {
			return null;
		}
		List<T> list = FastJsonUtils.fromJsonArray(result, resultClass);
		return list;
	}
	
	/**
	 * 执行get请求
	 * @param url 请求的url，不含前缀
	 * @param params 参数
	 * @return 调用结果
	 */
	public static JSONObject getJsonObject(String url, Map<String, String> params) {
		byte[] result = get(url, params);
		if (result == null) {
			return null;
		}
		Object obj = JSON.parse(result);
        if (obj instanceof JSONObject) {
            return (JSONObject) obj;
        }
        return (JSONObject) JSON.toJSON(obj);
	}
	
	/**
	 * 执行get请求
	 * @param url 请求的url，不含前缀
	 * @param resultClass 结果泛型类
	 * @return 调用结果
	 */
	public static <T> T getBean(String url, Class<T> resultClass) {
		byte[] result = get(url, null);
		if (result == null) {
			return null;
		}
		T t = FastJsonUtils.fromJson(result, resultClass);
		return t;
	}
	
	/**
	 * 执行get请求
	 * @param url 请求的url，不含前缀
	 * @param resultClass 结果泛型类
	 * @return 调用结果
	 */
	public static <T> List<T> getList(String url, Class<T> resultClass) {
		byte[] result = get(url, null);
		if (result == null) {
			return null;
		}
		List<T> list = FastJsonUtils.fromJsonArray(result, resultClass);
		return list;
	}
	
	/**
	 * 执行get请求
	 * @param url 请求的url，不含前缀
	 * @return 调用结果
	 */
	public static JSONObject getJsonObject(String url) {
		byte[] result = get(url, null);
		if (result == null) {
			return null;
		}
		Object obj = JSON.parse(result);
        if (obj instanceof JSONObject) {
            return (JSONObject) obj;
        }
        return (JSONObject) JSON.toJSON(obj);
	}
	
	/**
	 * 执行post请求
	 * @param url 请求的url，不含前缀
	 * @param params 参数
	 * @param resultClass 结果泛型类
	 * @return 调用结果
	 */
	public static <T> T postBean(String url, Map<String, String> params, Class<T> resultClass) {
		byte[] result = post(url, params);
		T t = FastJsonUtils.fromJson(result, resultClass);
		return t;
	}
	
	/**
	 * 执行post请求
	 * @param url 请求的url，不含前缀
	 * @param params 参数
	 * @param resultClass 结果泛型类
	 * @return 调用结果
	 */
	public static <T> List<T> postList(String url, Map<String, String> params, Class<T> resultClass) {
		byte[] result = post(url, params);
		List<T> list = FastJsonUtils.fromJsonArray(result, resultClass);
		return list;
	}
	
	/**
	 * 执行post请求
	 * @param url 请求的url，不含前缀
	 * @param params 参数
	 * @return 调用结果
	 */
	public static JSONObject postJsonObject(String url, Map<String, String> params) {
		byte[] result = post(url, params);
		Object obj = JSON.parse(result);
        if (obj instanceof JSONObject) {
            return (JSONObject) obj;
        }
        return (JSONObject) JSON.toJSON(obj);
	}
	
	/**
	 * 执行get请求
	 * @param url 请求的url，不含前缀
	 * @return 调用结果
	 */
	public static byte[] get(String url) {
		return invoke(url, null, HttpMethod.GET, true);
	}
	
	/**
	 * 执行get请求
	 * @param url 请求的url，不含前缀
	 * @param params 参数
	 * @return 调用结果
	 */
	public static byte[] get(String url, Map<String, String> params) {
		return invoke(url, params, HttpMethod.GET, BRIEF);
	}
	/**
	 * 执行get请求
	 * @param url 请求的url
	 * @param brief true简介url，不含http://ip:port，false全路径
	 * @return 调用结果
	 */
	public static byte[] get(String url, boolean brief) {
		return invoke(url, null, HttpMethod.GET, brief);
	}
	
	/**
	 * 执行get请求
	 * @param url 请求的url
	 * @param brief true简介url，不含http://ip:port，false全路径
	 * @param params 参数
	 * @return 调用结果
	 */
	public static byte[] get(String url, Map<String, String> params, boolean brief) {
		return invoke(url, params, HttpMethod.GET, brief);
	}
	
	/**
	 * 执行post请求，参数是普通的http参数，键值对
	 * @param url 请求的url，不含前缀
	 * @param params 参数
	 * @return 调用结果
	 */
	public static byte[] post(String url) {
		return invoke(url, null, HttpMethod.POST, BRIEF);
	}
	
	/**
	 * 执行post请求，参数是普通的http参数，键值对
	 * @param url 请求的url，不含前缀
	 * @param params 参数
	 * @return 调用结果
	 */
	public static byte[] post(String url, Map<String, String> params) {
		return invoke(url, params, HttpMethod.POST, BRIEF);
	}
	
	/**
	 * 执行post请求，参数是普通的http参数，键值对
	 * @param url 请求的url
	 * @param brief true简介url，不含http://ip:port，false全路径
	 * @return 调用结果
	 */
	public static byte[] post(String url, boolean brief) {
		return invoke(url, null, HttpMethod.POST, brief);
	}
	
	/**
	 * 执行post请求，参数是普通的http参数，键值对
	 * @param url 请求的url
	 * @param params 参数
	 * @param brief true简介url，不含http://ip:port，false全路径
	 * @return 调用结果
	 */
	public static byte[] post(String url, Map<String, String> params, boolean brief) {
		return invoke(url, params, HttpMethod.POST, brief);
	}
	
	/**
	 * 发起http调用，参数是普通的http参数，键值对
	 * @param url 请求的url，不含前缀
	 * @param params 请求参数
	 * @param method 请求方式，post、get等
	 * @param brief true简介url，不含http://ip:port，false全路径
	 * @return 请求结果
	 */
	private static byte[] invoke(String url, Map<String, String> params, HttpMethod method, boolean brief) {
		try {
			StringBuilder sb = new StringBuilder();
			if (brief) { // 没有前缀
				sb.append(HTTPS_PREFIX).append(url);
			} else { // 有前缀
				sb.append(url);
			}
			if (method == HttpMethod.GET && params != null && params.size() > 0) {
				sb.append(params(params, true));
			}
			URL httpsUrl = new URL(sb.toString());
			HttpsURLConnection connection = getHttpsConnection(httpsUrl, method);
			connection.connect();
			if (method == HttpMethod.POST && params != null && params.size() > 0) {
				OutputStream os = connection.getOutputStream();
				os.write(params(params));
				os.flush();
			}
			
			int code = connection.getResponseCode();
			if (code == 200) {
				InputStream inputStream = connection.getInputStream();
				byte[] response = IOUtils.toByteArray(inputStream);
				if (LOGGER.isDebugEnabled()) {
					LOGGER.debug(new String(response, Char.UTF8));
				}
				return response;
			} else {
				LOGGER.error("invoke error, code=[{}]", code);
			}
		} catch (MalformedURLException e) {
			LOGGER.error("url format error, msg=[{}].", e.getMessage());
		} catch (IOException e) {
			LOGGER.error("http connection io error., msg=[{}].", e);
		}
		return null;
	}
	
	/**
	 * 发起http调用，参数是普通的http参数，键值对
	 * @param url 请求的url，不含前缀
	 * @param method 请求方式，post、get等
	 * @param params 请求参数
	 * @param brief true简介url，不含http://ip:port，false全路径
	 * @return 请求结果
	 */
	private static InputStream invoke(String url, HttpMethod method, Map<String, String> params, boolean brief) {
		try {
			StringBuilder sb = new StringBuilder();
			if (brief) { // 没有前缀
				sb.append(HTTPS_PREFIX).append(url);
			} else { // 有前缀
				sb.append(url);
			}
			if (method == HttpMethod.GET && params != null && params.size() > 0) {
				sb.append(params(params, true));
			}
			URL httpsUrl = new URL(sb.toString());
			HttpsURLConnection connection = getHttpsConnection(httpsUrl, method);
			connection.connect();
			if (method == HttpMethod.POST && params != null && params.size() > 0) {
				OutputStream os = connection.getOutputStream();
				os.write(params(params));
				os.flush();
			}
			
			int code = connection.getResponseCode();
			if (code == 200) {
				InputStream inputStream = connection.getInputStream();
				
				if (LOGGER.isDebugEnabled()) {
					LOGGER.debug("http request success.");
				}
				return inputStream;
			} else {
				LOGGER.error("invoke error, code=[{}]", code);
			}
		} catch (MalformedURLException e) {
			LOGGER.error("url format error, msg=[{}].", e.getMessage());
		} catch (IOException e) {
			LOGGER.error("http connection io error., msg=[{}].", e);
		}
		return null;
	}
	
	/**
	 * 执行post请求，参数是json格式。
	 * @param url 请求的url，不含前缀
	 * @param params 参数
	 * @param resultClass 结果泛型类
	 * @return 调用结果
	 */
	public static <T> T postJsonBean(String url, Object params, Class<T> resultClass) {
		byte[] result = postJson(url, params);
		T t = FastJsonUtils.fromJson(result, resultClass);
		return t;
	}
	
	/**
	 * 执行post请求，参数是json格式。
	 * @param url 请求的url，不含前缀
	 * @param params 参数
	 * @param resultClass 结果泛型类
	 * @return 调用结果
	 */
	public static <T> List<T> postJsonList(String url, Object params, Class<T> resultClass) {
		byte[] result = postJson(url, params);
		List<T> list = FastJsonUtils.fromJsonArray(result, resultClass);
		return list;
	}
	
	/**
	 * 执行post请求，参数是json格式。
	 * @param url 请求的url，不含前缀
	 * @param params 参数
	 * @return 调用结果
	 */
	public static JSONObject postJsonObject(String url, Object params) {
		byte[] result = postJson(url, params);
		Object obj = JSON.parse(result);
        if (obj instanceof JSONObject) {
            return (JSONObject) obj;
        }
        return (JSONObject) JSON.toJSON(obj);
	}
	
	/**
	 * 执行post请求，参数是json格式。
	 * @param url 请求的url，不含前缀
	 * @param params 参数
	 * @return 调用结果
	 */
	public static byte[] postJson(String url, Object params) {
		return invokeJson(url, params, HttpMethod.POST, BRIEF);
	}
	
	/**
	 * 发起https调用，参数是json格式
	 * @param url 请求的url，不含前缀
	 * @param params 请求参数
	 * @param method 请求方式，post、get等
	 * @return 请求结果
	 */
	private static byte[] invokeJson(String url, Object params, HttpMethod method, boolean brief) {
		try {
			StringBuilder sb = new StringBuilder();
			if (brief) { // 没有前缀
				sb.append(HTTPS_PREFIX).append(url);
			} else { // 有前缀
				sb.append(url);
			}
			
			URL httpsUrl = new URL(sb.toString());
			HttpsURLConnection connection = getHttpsConnection(httpsUrl, method);
			connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
			connection.connect();
			if (params != null) {
				OutputStream os = connection.getOutputStream();
				if (params instanceof String) {
					os.write(params.toString().getBytes(Char.UTF8));
				} else {
					os.write(FastJsonUtils.toJsonBytes(params));
				}
				os.flush();
			}
			
			int code = connection.getResponseCode();
			if (code == 200) {
				InputStream inputStream = connection.getInputStream();
				byte[] response = IOUtils.toByteArray(inputStream);
				if (LOGGER.isDebugEnabled()) {
					LOGGER.debug(new String(response, Char.UTF8));
				}
				return response;
			} else {
				LOGGER.error("invoke error, code=[{}]", code);
			}
		} catch (MalformedURLException e) {
			LOGGER.error("url format error, msg=[{}].", e.getMessage());
		} catch (IOException e) {
			LOGGER.error("http connection io error., msg=[{}].", e);
		}
		return null;
	}
	
	/**
	 * 将参数组装成键值对，兼容post（不需要问号）和get
	 * @param params 请求参数
	 * @param append 是否拼接问号?
	 * @return 类似key=value&key1=value1
	 */
	private static String params(Map<String, String> params, boolean append) {
		if (params == null || params.size() == 0) {
			return "";
		}
		StringBuilder builder = new StringBuilder();
		boolean first = true;
		for (Entry<String, String> entry : params.entrySet()) {
			if (first) {
				if (append) {
					builder.append("?");
				}
				builder.append(entry.getKey()).append("=").append(entry.getValue());
				first = false;
			} else {
				builder.append("&").append(entry.getKey()).append("=").append(entry.getValue());
			}
		}
		String postParams = builder.toString();
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug(postParams);
		}
		return postParams;
	}
	
	/**
	 * 将参数组装成键值对，兼容post（不需要问号）和get
	 * @param params 请求参数
	 * @return 类似key=value&key1=value1, byte[]
	 */
	private static byte[] params(Map<String, String> params) {
		String p = params(params, false);
		if (p == null || p.equals("")) {
			return ByteUtils.ZERO;
		}
		return p.getBytes(Char.UTF8);
	}
	
	/**
	 * 上传文件，以及普通文本参数。
	 * @param url url
	 * @param params 参数，key是参数名，value是参数值
	 * @return 调用结果
	 */
	public static byte[] uploadFile(final String url, final Map<String, Object> params) {
		try {
			URL httpsUrl = new URL(HTTPS_PREFIX + url);
			HttpsURLConnection connection = getHttpsConnection(httpsUrl);
			connection.connect();
			if (params != null) {
				OutputStream os = connection.getOutputStream();
				DataOutputStream dos = new DataOutputStream(os);
				for (Entry<String, Object> entry : params.entrySet()) {
					Object body = entry.getValue();
					if (body instanceof File) {
						File file = (File) body;
						writeFile(dos, entry.getKey(), file);
					} else {
						writeText(dos, entry.getKey(), body.toString());
					}
				}
				// 添加结束标志
				end(dos);
				
				os.flush();
				dos.flush();
			}
			
			int code = connection.getResponseCode();
			if (code == 200) {
				InputStream inputStream = connection.getInputStream();
				byte[] response = IOUtils.toByteArray(inputStream);
				if (LOGGER.isDebugEnabled()) {
					LOGGER.debug(new String(response, Char.UTF8));
				}
				return response;
			} else {
				LOGGER.error("invoke error, code=[{}]", code);
			}
		} catch (MalformedURLException e) {
			LOGGER.error("url format error, msg=[{}].", e.getMessage());
		} catch (IOException e) {
			LOGGER.error("http connection io error., msg=[{}].", e);
		}
		return null;
	}
	
	/**
	 * 上传文件，以及普通文本参数。
	 * @param url url
	 * @param params 参数，key是参数名，value是参数值
	 * @param params 返回结果泛型类
	 * @return 调用结果
	 */
	public static <T> T uploadFile(final String url, final Map<String, Object> params, Class<T> resultClass) {
		byte[] result = uploadFile(url, params);
		return FastJsonUtils.fromJson(result, resultClass);
	}
	
	/**
	 * 写入文本数据
	 * @param dos 数据流
	 * @param name 字段名
	 * @param value 字段值
	 * @throws IOException
	 */
	private static void writeText(DataOutputStream dos, String name, String value) throws IOException {
		dos.writeBytes(Consts.START);
		dos.writeBytes(Consts.BOUNDARY);
		dos.writeBytes(Consts.END);
		dos.writeBytes("Content-Disposition: form-data; name=\"" + name + "\"");
		dos.writeBytes(Consts.END);
		dos.writeBytes(Consts.END);
		dos.writeBytes(value);
		dos.writeBytes(Consts.END);
	}
    
	/**
	 * 写入文件数据
	 * @param dos 数据流
	 * @param name 文件名字段
	 * @param file 文件
	 * @throws IOException
	 */
	private static void writeFile(DataOutputStream dos, String name, File file) throws IOException {
		dos.writeBytes(Consts.START);
		dos.writeBytes(Consts.BOUNDARY);
		dos.writeBytes(Consts.END);
		dos.writeBytes("Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" + name + "\"");
		dos.writeBytes(Consts.END);
		dos.writeBytes("Content-Type: application/octet-stream");
		dos.writeBytes(Consts.END);
		dos.writeBytes(Consts.END);
		dos.write(IOUtils.toByteArray(file));
		dos.writeBytes(Consts.END);
	}
	
	/**
	 * 添加结尾数据
	 * @param dos
	 * @throws IOException
	 */
	private static void end(DataOutputStream dos) throws IOException {
		dos.writeBytes(Consts.START);
		dos.writeBytes(Consts.BOUNDARY);
		dos.writeBytes(Consts.START);
		dos.writeBytes(Consts.END);
		dos.writeBytes(Consts.END);
	}
	
	/**
	 * post请求下载文件，返回byte[].
	 * @param url 下载的url，不含前缀
	 * @param params 参数
	 * @return 文件字节数组
	 */
	public byte[] download(final String url, final Map<String, String> params) {
		return invoke(url, params, HttpMethod.POST, true);
	}
	
	/**
     * 文件下载，记得关闭流。
     * @param url 下载的url，不含前缀
     * @param params 参数
     * @return 文件流
     */
    public static InputStream download(final Map<String, String> params, final String url) {
        return invoke(url, HttpMethod.POST, params, true);
    }
	
    /**
	 * post请求下载文件，返回byte[].
	 * @param url 下载的url，全路径，不含参数和？
	 * @param params 参数
	 * @return 文件字节数组
	 */
	public byte[] loadFile(final String url, final Map<String, String> params) {
		return invoke(url, params, HttpMethod.POST, false);
	}
	
	/**
     * 文件下载，记得关闭流。
     * @param url 下载的url，全路径，不含参数和？
     * @param params 参数
     * @return 文件流
     */
    public static InputStream loadFile(final Map<String, String> params, final String url) {
        return invoke(url, HttpMethod.POST, params, false);
    }
	
	public static void main(String[] args) {
//		try {
//			URL url = new URL("https://localhost:8443/jrt_org_admin/employee/login");
//			HttpsURLConnection connection = getHttpsURLConnection(url, HttpMethod.POST);
//			connection.connect();
//			int code = connection.getResponseCode();
//			if (code == 200) {
//				InputStream inputStream = connection.getInputStream();
//				byte[] response = IOUtils.toByteArray(inputStream);
//				if (LOGGER.isDebugEnabled()) {
//					LOGGER.debug(new String(response, Char.UTF8));
//				}
//			} else {
//				LOGGER.error("invoke error, code=[{}]", code);
//			}
//		} catch (MalformedURLException e) {
//			LOGGER.error("url format error, msg=[{}].", e.getMessage());
//		} catch (IOException e) {
//			LOGGER.error("https connection io error., msg=[{}].", e);
//		}
	}
}
